package org.hxyjs.interceptor;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

/**
 * Created by chenboge on 2017/5/7.
 * <p>
 * Email:baigegechen@gmail.com
 * <p>
 * description:
 */

/**
 * @Intercepts 说明是一个拦截器
 * @Signature 拦截器的签名 type 拦截的类型 四大对象之一(
 *            Executor,ResultSetHandler,ParameterHandler,StatementHandler)
 *            method 拦截的方法 args 参数
 */
@Component
//@Intercepts({
//		@Signature(type = StatementHandler.class,method = "parameterize",args = java.sql.Statement.class)
//})

@Intercepts( {
		@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class,Integer.class}) })
public class PageInterceptor implements Interceptor {

	// 每页显示的条目数
	private int pageSize;
	// 当前现实的页数
	private int currPage;

	private String dbType;

	//invokcation就是包含了 被拦截的对象 sql语句执行对象 StatementHandler 和 执行的方法
	@Override
	public Object intercept(Invocation invocation) throws Throwable {

		// 获取StatementHandler，默认是RoutingStatementHandler
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		// 获取statementHandler包装类
		MetaObject metaObjectHandler = SystemMetaObject.forObject(statementHandler);

		// 分离代理对象链
		while (metaObjectHandler.hasGetter("h")) {
			Object obj = metaObjectHandler.getValue("h");
			metaObjectHandler = SystemMetaObject.forObject(obj);
		}

		while (metaObjectHandler.hasGetter("target")) {
			Object obj = metaObjectHandler.getValue("target");
			metaObjectHandler = SystemMetaObject.forObject(obj);
		}

		// 获取连接对象
		// Connection connection = (Connection) invocation.getArgs()[0];

		// object.getValue("delegate"); 获取StatementHandler的实现类

		// 获取查询接口映射的相关信息
		MappedStatement mappedStatement = (MappedStatement) metaObjectHandler.getValue("delegate.mappedStatement");
		String mapId = mappedStatement.getId();

		// statementHandler.getBoundSql().getParameterObject();

		// 拦截以.ByPage结尾的请求，分页功能的统一实现
		if (mapId.matches(".+ByPage$")) {//如果mybatis要执行的方法名包含了ByPage
			System.out.println("--------------拦截器工作-------------");
			// 获取进行数据库操作时管理参数的handler
			ParameterHandler parameterHandler = (ParameterHandler) metaObjectHandler
					.getValue("delegate.parameterHandler");
			// 获取请求时的参数
			Map<String, Object> paraObject = (Map<String, Object>) parameterHandler.getParameterObject(); //获取方法的参数
			// 也可以这样获取
			// paraObject = (Map<String, Object>)
			// statementHandler.getBoundSql().getParameterObject();

			// 参数名称和在service中设置到map中的名称一致    得到的是PageInfo对象成员变量的值拷贝给map
			currPage = (int) paraObject.get("currPage");
			pageSize = (int) paraObject.get("pageSize");

			String sql = (String) metaObjectHandler.getValue("delegate.boundSql.sql");//mybatis是将mapper.xml文件中的语句绑定到接口的实现类
			//java代码调用commentMapper.queryPage方法，但是没有commentMapper的实现类，但是有commentMapper.xml，加载commentMapper.xml,遍历接口的method找出和
			//xml中id一样的select 将其中的sql语句保存到  delegate.boundSql.sql，此时取出的sql语句是 select * from comment不带limit
			// 也可以通过statementHandler直接获取
			// sql = statementHandler.getBoundSql().getSql();

			// 构建分页功能的sql语句
			String limitSql;
			sql = sql.trim();
			limitSql = sql + " limit " + (currPage - 1) * pageSize + "," + pageSize;//在执行sql之前 给sql拼接了 limit语句实现分页
			System.out.println("--------666-------- "+limitSql+" ----------666--------");
			// 将构建完成的分页sql语句赋值个体'delegate.boundSql.sql'，偷天换日
			metaObjectHandler.setValue("delegate.boundSql.sql", limitSql);
		}
		// 调用原对象的方法，进入责任链的下一级
		return invocation.proceed();//这个时候才执行sql
	}

	// 获取代理对象
	@Override
	public Object plugin(Object o) {
		// 生成object对象的动态代理对象
		return Plugin.wrap(o, this);
	}

	// 设置代理对象的参数
	@Override
	public void setProperties(Properties properties) {
		// 如果项目中分页的pageSize是统一的，也可以在这里统一配置和获取，这样就不用每次请求都传递pageSize参数了。参数是在配置拦截器时配置的。
		String limit1 = properties.getProperty("limit", "2");
		this.pageSize = Integer.valueOf(limit1);
		this.dbType = properties.getProperty("dbType", "mysql");
	}
}
